package edu.ccut.saturn.studio.core.search;
/**
 *修改了583——615行 
 */

import java.io.File;
import java.io.FileFilter;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import edu.ccut.saturn.studio.core.exception.XMLException;
import edu.ccut.saturn.studio.core.saturnproperty.StringUtils;
import edu.ccut.saturn.studio.core.search.cache.SaturnSearchCache;
import edu.ccut.saturn.studio.core.util.eclipse.SaturnBundleUtil;
import edu.ccut.saturn.studio.core.util.eclipse.SaturnEclipsePathUtil;
import edu.ccut.saturn.studio.core.util.eclipse.SaturnProjectUtil;
import edu.ccut.saturn.studio.core.util.xml.SaturnW3CUtil;

public class SaturnFileSearcher {

	protected static final String SEPARATOR = File.separator;

	public static final String CLASSPATH_IS_TYPE_SRC = "src";
	public static final String CLASSPATH_IS_TYPE_LIB = "lib";
	public static final String SEARCH_NAME_ENDWITH_JAR = "jar";
	private static final String TYPE_FILE_NAME = ".saturn";
	private static final String SATURN_FOLDER_TYPE = "type";
	protected static final String SATURN_FOLDER_MODULE = "module";
	protected static final String MANIFEST_BUNDLE_REQUIRE = "Require-Bundle";

	protected static SaturnSearchCache cache = SaturnSearchCache.getInstance(); // 获得缓存对象

	private SaturnFileSearcher() {

	}

	public static class SearchResult implements Serializable,
			Comparable<SearchResult> {
		private static final long serialVersionUID = 1066682749151745257L;

		private File file;

		private Document document;

		private String bundleName;

		private String fileName;

		private IProject project;

		private String srcName;

		public String getKey() {

			if (this.fileName.endsWith(".logic")
					|| this.fileName.endsWith(".action")) {

				Node InformationNode = SaturnW3CUtil.getFirstChildByName(
						this.document.getDocumentElement(), "information");
				Node KeyNode = SaturnW3CUtil.getFirstChildByName(
						InformationNode, "key");
				String keyValue = SaturnW3CUtil.getNodeValue(KeyNode);
				return bundleName + "::" + keyValue;

			}
			if (this.fileName.endsWith(".wf")) {
				Node InformationNode = SaturnW3CUtil.getFirstChildByName(
						this.document.getDocumentElement(), "information");
				Node KeyNode = SaturnW3CUtil.getFirstChildByName(
						InformationNode, "key");
				String keyValue = SaturnW3CUtil.getNodeValue(KeyNode);
				return keyValue;
			}
			if (this.fileName.endsWith(".component")) {

				Node ComponentNode = SaturnW3CUtil.getFirstChildByName(
						this.document.getDocumentElement(), "component");
				String keyValue = SaturnW3CUtil.getNodeAttributeValue(
						ComponentNode, "key");
				return bundleName + "::" + keyValue;

			}
			if (this.fileName.endsWith(".saturndata")) {
				Node dataNode = SaturnW3CUtil.getFirstChildByName(this.document
						.getDocumentElement(), "data");
				String namespace = SaturnW3CUtil.getNodeAttributeValue(dataNode,
						"namespace");
				String name = SaturnW3CUtil.getNodeAttributeValue(dataNode,
						"name");
				return namespace + "::" + name;
			}
			if (this.fileName.endsWith(".validate")) {
				Node ComponentNode = SaturnW3CUtil.getFirstChildByName(
						this.document.getDocumentElement(), "validate");
				String keyValue = SaturnW3CUtil.getNodeAttributeValue(
						ComponentNode, "key");
				return bundleName + "::" + keyValue;
			}

			return null;
		}

		public IProject getProject() {
			return project;
		}

		public void setProject(IProject project) {
			this.project = project;
		}

		public String getSrcName() {
			return srcName;
		}

		public void setSrcName(String srcName) {
			this.srcName = srcName;
		}

		public Document getDocument() {
			return document;
		}

		public void setDocument(Document document) {
			this.document = document;
		}

		public String getBundleName() {
			return bundleName;
		}

		public void setBundleName(String bundleName) {
			this.bundleName = bundleName;
		}

		public File getFile() {
			return file;
		}

		public String getFileName() {
			return this.fileName;
		}

		public void setFile(File file) {
			this.file = file;
		}

		public void setFileName(String fileName) {
			this.fileName = fileName;
		}

		@Override
		public String toString() {
			return fileName + " - " + bundleName;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result
					+ ((bundleName == null) ? 0 : bundleName.hashCode());
			result = prime * result
					+ ((fileName == null) ? 0 : fileName.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			final SearchResult other = (SearchResult) obj;
			if (bundleName == null) {
				if (other.bundleName != null)
					return false;
			} else if (!bundleName.equals(other.bundleName))
				return false;
			if (fileName == null) {
				if (other.fileName != null)
					return false;
			} else if (!fileName.equals(other.fileName))
				return false;
			return true;
		}

		public int compareTo(SearchResult searchResult) {
			return this.fileName.compareTo(searchResult.fileName);
		}
	}

	public static List<SearchResult> getValueAndDocumentMapInWorkspace(
			String suffixes, IObserver observer, IProgressMonitor monitor)
			throws Exception {

		return getValueAndDocumentMapInWorkspace(new String[] { suffixes },
				observer, monitor);
	}

	public static List<SearchResult> getValueAndDocumentMapInWorkspace(
			String[] suffixes, IObserver observer, IProgressMonitor monitor)
			throws Exception {

		List<SearchResult> results = new ArrayList<SearchResult>();

		IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
				.getProjects();
		
		if (monitor != null) {
			int totalProjectFileNumber = 0;

			for (IProject project : projects) {
				Set<String> srcs = SaturnProjectUtil.getClassPathsByType(project,
						CLASSPATH_IS_TYPE_SRC);
				for (String suffix : suffixes) {
					int totalFileNumber = getSrcTotalParseFilesNumber(suffix,
							srcs);
					totalFileNumber += getRequireBundles(project).length;

					totalProjectFileNumber += totalFileNumber;
				}
			}

			monitor.beginTask("start parse file:", totalProjectFileNumber);
		}

		Set<SearchResult> jarResult = new HashSet<SearchResult>();

		for (IProject project : projects) {
			if (project.hasNature("cn.edu.ccut.saturncontent.ProjectNature")) {
				for (String suffix : suffixes) {
					List<SearchResult> projectResults = getValueAndDocumentMapInProjectWithOutJar(
							project, suffix, monitor);

					results.addAll(projectResults);
					jarResult.addAll(searchProjectJar(project, suffix,
							monitor));
					
				}
			}
		}

		results.addAll(jarResult);
		Collections.sort(results);
		if (observer != null) {
			observer.update(results);
		}

		if (monitor != null) {
			monitor.done();
		}

		return results;
	}

	public static List<SearchResult> getValueAndDocumentMapInProject(
			IProject project, final String[] suffixes, IObserver observer,
			IProgressMonitor monitor) throws Exception {
				List<SearchResult> results = new ArrayList<SearchResult>();
			for (String suffix : suffixes) {
				results = getValueAndDocumentMapInProjectWithOutJar(
						project, suffix, monitor);
				List<SearchResult> jarResult = searchProjectJar(project, suffix,
						monitor);
				results.addAll(jarResult);
				Collections.sort(results);
				if (observer != null) {
					observer.update(results);
				}
				cache.writeObject();
				
			}
			return results;
		}
			
			public static List<SearchResult> getValueAndDocumentMapInProject(
					IProject project, final String suffix, IObserver observer,
					IProgressMonitor monitor) throws Exception {
				return getValueAndDocumentMapInProject(project, new String[]{suffix}, observer, monitor);
		}
	
	public static List<SearchResult> getValueAndDocumentMapInProjectWithOutJar(
			IProject project, final String[] suffixs, IProgressMonitor monitor)
			throws Exception {
		
		List<SearchResult> results = new ArrayList<SearchResult>();
		
		if (suffixs != null) {
			for (String suffix : suffixs) {
					results.addAll(getValueAndDocumentMapInProjectWithOutJar(project, suffix, monitor));
			}
		}
		
		return results;
	}

	public static List<SearchResult> getValueAndDocumentMapInProjectWithOutJar(
			IProject project, final String suffix, IProgressMonitor monitor)
			throws Exception {

		List<SearchResult> results = new ArrayList<SearchResult>();

		Set<String> srcs = SaturnProjectUtil.getClassPathsByType(project, CLASSPATH_IS_TYPE_SRC);

//		if (srcs.size()!=0) {
			for (String src : srcs) {
				for (File file : new File(src).listFiles()) {

					if (file.isDirectory()) {
						if (isSaturnTypeDirectory(file, SATURN_FOLDER_MODULE)) {

							for (File subFile : file.listFiles()) {
								if (subFile.isDirectory()
										&& isSaturnTypeDirectory(subFile, suffix)) {

									searchDirectory(project, src, subFile,
											suffix, results, monitor);
								}
							}
						}

						if (isSaturnTypeDirectory(file, suffix)) {
							searchDirectory(project, src, file, suffix,
									results, monitor);
						}
					}
					if (suffix == "config") {
						String bundleName = getBundleName(project);
						if (file.isFile() && isVaildSuffix(suffix, file.getName())) {
							Document doc = null;

							if (monitor != null) {
								monitor.setTaskName("parse file -> "
										+ file.getAbsolutePath());
							}

							SearchResult result = new SearchResult();
							result.setBundleName(bundleName);
							result.setFile(file);
							result.setFileName(file.getName());
							result.setDocument(doc);
							result.setProject(project);
							result.setSrcName(src);

							results.add(result);
							if (monitor != null) {
								monitor.internalWorked(1);
							}
						}
					}
				}
			}
//		}
		return results;
	}

	private static int getSrcTotalParseFilesNumber(final String suffix,
			Set<String> srcs) {
		int totalFile = 0;
		for (String src : srcs) {
			for (File file : new File(src).listFiles()) {
				if (file.isDirectory()) {
					if (isSaturnTypeDirectory(file, SATURN_FOLDER_MODULE)) {

						for (File subFile : file.listFiles()) {
							if (subFile.isDirectory()
									&& isSaturnTypeDirectory(subFile, suffix)) {

								File[] files = subFile
										.listFiles(new FileFilter() {
											public boolean accept(File pathname) {
												return pathname.getPath()
														.endsWith(suffix);
											}
										});

								totalFile += files.length;
							}
						}
					}

					if (isSaturnTypeDirectory(file, suffix)) {
						File[] files = file.listFiles(new FileFilter() {
							public boolean accept(File pathname) {
								return pathname.getPath().endsWith(suffix);
							}
						});

						totalFile += files.length;
					}
				}
			}
		}

		return totalFile;
	}

	private static void searchDirectory(IProject project, String srcName,
			File directory, String suffix, List<SearchResult> results,
			IProgressMonitor monitor) throws Exception {

		File[] files = directory.listFiles();
		String bundleName = getBundleName(project);

		for (File file : files) {
			if (file.isFile() && isVaildSuffix(suffix, file.getName())) {
				Document doc = null;
				try {
					if (!file.getName().endsWith(".jsp")) {
						doc = SaturnW3CUtil.getDocument(file.getAbsolutePath());
					}
				} catch (Exception e) {
					  // 不处理异常，此处
				}

				if (monitor != null) {
					monitor.setTaskName("parse file -> "
							+ file.getAbsolutePath());
				}

				SearchResult result = new SearchResult();
				result.setBundleName(bundleName);
				result.setFile(file);
				result.setFileName(file.getName());
				result.setDocument(doc);
				result.setProject(project);
				result.setSrcName(srcName);

				results.add(result);
				if (monitor != null) {
					monitor.internalWorked(1);
				}
			}
		}
	}

	private static boolean isVaildSuffix(String suffix, String fileName) {
		if (suffix.indexOf(",") > 0) {
			String[] suffixs = suffix.split(",");

			for (String one : suffixs) {
				if (fileName.endsWith(one)) {
					return true;
				}
			}

			return false;
		}

		return fileName.endsWith(suffix);
	}

	private static List<SearchResult> searchProjectJar(IProject project,
			String suffix, IProgressMonitor monitor) throws Exception {

		List<SearchResult> results = new ArrayList<SearchResult>();

		String[] bundles = getRequireBundles(project);

		if (bundles == null || bundles.length == 0) {
			return results;
		}

		File eclipsePluginFile = SaturnEclipsePathUtil
		      .getEclipsePluginDirectory();
				
		int i = 0;
		try {
			for (File file : eclipsePluginFile.listFiles()) {
				String jarFileName = file.getName();
				for (String bundle : bundles) {
					if (jarFileName.contains(bundle)) {
						i ++;
					} 
				}
			}
		} catch (RuntimeException e1) {
			e1.printStackTrace();
		}
		if(i == 0) {
			 eclipsePluginFile = SaturnEclipsePathUtil
			.getSaturnEclipsePluginDirectory();
		}

		Map<String, SaturnSearchJarInfo> cacheMap = cache.getJarCache().get(
				suffix);

		if (cacheMap == null) {
			cacheMap = new HashMap<String, SaturnSearchJarInfo>();
			cache.getJarCache().put(suffix, cacheMap);
		}

		for (File file : eclipsePluginFile.listFiles()) {
			String jarFileName = file.getName();
			if (file.isFile() && jarFileName.endsWith("jar")) {
				for (String bundle : bundles) {
					if (jarFileName.contains(bundle)) {

						if (monitor != null) {
							monitor.setTaskName("parse file -> "
									+ file.getAbsolutePath());
						}
						SaturnSearchJarInfo jarInfo = cacheMap.get(jarFileName);

						if (jarInfo != null) {
							if (jarInfo.getJarLastModifyTime() == file
									.lastModified()) {
								results.addAll(jarInfo.getJarFileInfo());
								if (monitor != null) {
									monitor.internalWorked(1);
								}
								break;
							} else {
								cacheMap.remove(jarFileName);
							}
						}

						List<SearchResult> oneJarResult = new ArrayList<SearchResult>();

						JarFile jar = new JarFile(file);
						Enumeration<JarEntry> jarEntries = jar.entries();
						String bundleName = SaturnBundleUtil
								.getBundleSymbolicName(jar);

						while (jarEntries.hasMoreElements()) {
							ZipEntry ze = (ZipEntry) jarEntries.nextElement();
							String fileName = ze.getName();

							if (isVaildSuffix(suffix, fileName)) {
								InputStream in = jar.getInputStream(ze);
								Document doc = null;
								try {
									doc = SaturnW3CUtil.getDocument(in);
								} catch (Exception e) {
									  // 不处理异常，此处
								}

								if (monitor != null) {
									monitor.setTaskName("parse file -> "
											+ file.getAbsolutePath());
								}

								SearchResult result = new SearchResult();
								result.setBundleName(bundleName);
								result.setFile(file);

								int index = fileName.lastIndexOf("/");

								if (index >= 0) {
									fileName = fileName.substring(index + 1);
								}
								result.setFileName(fileName);
								result.setDocument(doc);

								oneJarResult.add(result);
							}

						}
						results.addAll(oneJarResult);

						SaturnSearchJarInfo cacheInfo = new SaturnSearchJarInfo();
						cacheInfo.setJarName(file.getName());
						cacheInfo.setJarLastModifyTime(file.lastModified());
						cacheInfo.setJarFileInfo(oneJarResult);
						cacheMap.put(jarFileName, cacheInfo);

						if (monitor != null) {
							monitor.internalWorked(1);
						}
					}
				}
			}
		}

		return results;
	}

	private static String[] getRequireBundles(IProject project)
			throws Exception {
		String bundelPath = SaturnBundleUtil.getProjectBundleFile(project)
				.getPath();
		String requireBundles = SaturnBundleUtil.getBundleAttribute(bundelPath,
				MANIFEST_BUNDLE_REQUIRE);

		return requireBundles.split(",");
	}

	
	/**
	 * 从classpath文件中得到要的信息 src、lib的路径的数组
	 * 
	 * @param suffix
	 *            文件的类型
	 * @param kind
	 *            根据kind的类型得到数组
	 * 
	 */

	static public List<String> getClasspathInfo(IProject project, String type) {
		List<String> pathvalues = new ArrayList<String>();

		try {
			String classpath = (project.getLocation().toOSString() + SEPARATOR + ".classpath")
					.replace("/", SEPARATOR);
			Document doc = SaturnW3CUtil.getDocument(classpath);
			if (doc != null) {
				Element rootElement = doc.getDocumentElement();
				NodeList classpathentrynodes = rootElement
						.getElementsByTagName("classpathentry");
				for (int i = 0; i < classpathentrynodes.getLength(); i++) {
					String combineaccessrules = SaturnW3CUtil
							.getNodeAttributeValue(classpathentrynodes.item(i),
									"combineaccessrules");
					if (!combineaccessrules.equals("")) {
						continue;
					}
					String kindvalue = SaturnW3CUtil.getNodeAttributeValue(
							classpathentrynodes.item(i), "kind");
					if (kindvalue.equals(type)) {
						String pathvalue = SaturnW3CUtil.getNodeAttributeValue(
								classpathentrynodes.item(i), "path");
						pathvalues.add(pathvalue);
					}
				}
			}
		} catch (XMLException e) {
			e.printStackTrace();
		}
		return pathvalues;

	}

	/**
	 * 判断是不是指定类型的文件夹
	 * 
	 * @param path
	 *            文件夹路径
	 * @param type
	 *            要搜索文件类型
	 * @return
	 */
	protected static boolean isSaturnTypeDirectory(File path, String type) {
		String typeFilePath = path.getAbsolutePath() + SEPARATOR
				+ TYPE_FILE_NAME;

		File typeFile = new File(typeFilePath);

		if (typeFile.exists()) {

			try {
				Document doc = SaturnW3CUtil
						.getDocument(typeFilePath.toString());

				if (doc != null) {
					Node root = doc.getDocumentElement();

					String typeValue = SaturnW3CUtil.getChildNodeValue(root,
							SATURN_FOLDER_TYPE);

					if (type.indexOf(typeValue) >= 0
							|| (typeValue.equals("web") && type.indexOf("jsp") >= 0)
							|| (typeValue.equals("workflow") && type
									.indexOf("wf") >= 0)) {
						return true;
					}
				}
			} catch (XMLException e) {
				 // 不处理异常�
			}
		}

		return false;
	}

	/**
	 * 指定一个工程获得bundel属性文件里的BundelName
	 * 
	 * @param project
	 * @return
	 */
	protected static String getBundleName(IProject project) throws Exception {
		String path = SaturnBundleUtil.getProjectBundleFile(project).getPath();

		return SaturnBundleUtil.getBundleSymbolicName(path);
	}

	protected static String getNodeAttributeOrNodeValue(Document doc,
			String nodeName, String key) {

		if (doc != null) {
			Node root = doc.getDocumentElement();
			List<Node> nodes = SaturnW3CUtil.getChildrenByName(root, nodeName);

			if (nodes != null && nodes.isEmpty()) {
				nodes = SaturnW3CUtil.getChildrenByName(SaturnW3CUtil
						.getChildrenNode(root).get(0), nodeName);
			}

			for (Node node : nodes) {
				String attributeValue = SaturnW3CUtil.getNodeAttributeValue(
						node, key);

				if (StringUtils.isNotEmpty(attributeValue)) {
					return attributeValue;
				}

				String nodeValue = SaturnW3CUtil.getNodeValue(node);

				if (StringUtils.isNotEmpty(nodeValue)) {
					return nodeValue;
				}
			}
		}

		return "";
	}
}
